home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / plugins / mapdragmodes.py < prev    next >
Text File  |  2004-01-05  |  16KB  |  418 lines

  1. """   QuArK  -  Quake Army Knife
  2.  
  3. Additionnal mouse dragging modes (entity selecter, brush cutter, cube maker)
  4. """
  5. #
  6. # Copyright (C) 1996-99 Armin Rigo
  7. # THIS FILE IS PROTECTED BY THE GNU GENERAL PUBLIC LICENCE
  8. # FOUND IN FILE "COPYING.TXT"
  9. #
  10.  
  11. #$Header: /cvsroot/quark/runtime/plugins/mapdragmodes.py,v 1.7 2003/03/15 20:40:19 cdunde Exp $
  12.  
  13.  
  14.  
  15. Info = {
  16.    "plug-in":       "Mouse Drag Modes",
  17.    "desc":          "Entity selecter, brush cutter, cube maker.",
  18.    "date":          "28 dec 98",
  19.    "author":        "Armin Rigo",
  20.    "author e-mail": "arigo@planetquake.com",
  21.    "quark":         "Version 5.3" }
  22.  
  23.  
  24. import quarkx
  25. import quarkpy.qtoolbar
  26. import quarkpy.qhandles
  27. from quarkpy.maputils import *
  28. import quarkpy.maptools
  29. import quarkpy.maphandles
  30. import quarkpy.mapbtns
  31.  
  32.  
  33. ico_dict['ico_dragmodes'] = LoadIconSet1("mapdrm", 1.0)
  34.  
  35.  
  36. #
  37. # Additionnal drag modes (other plug-ins may add other drag modes).
  38. #
  39.  
  40. parent = quarkpy.qhandles.RectangleDragObject
  41.  
  42.  
  43. class EntRectSelDragObject(parent):
  44.     "A red rectangle that selects the entities it touches."
  45.  
  46.     Hint = hintPlusInfobaselink("Rectangular selection of ENTITIES||Rectangular selection of ENTITIES:\n\nThis works like 'rectangular selection of polyhedron' (just on the left), but selects entities instead of polyhedrons.", "intro.mapeditor.toolpalettes.mousemodes.html#selectentity")
  47.  
  48.     def rectanglesel(self, editor, x,y, rectangle):
  49.         if not ("T" in self.todo):
  50.             editor.layout.explorer.uniquesel = None
  51.         entlist = FindSelectable(editor.Root, ":e")
  52.         lastsel = None
  53.         for e in entlist:
  54.             org = e.origin
  55.             if org is None: continue
  56.             for f in rectangle.faces:
  57.                 if org*f.normal > f.dist:
  58.                     break
  59.             else: # the point is inside the polyhedron
  60.                 e.selected = 1
  61.                 lastsel = e
  62.         if lastsel is not None:
  63.             editor.layout.explorer.focus = lastsel
  64.             editor.layout.explorer.selchanged()
  65.  
  66. class EverythingRectSelDragObject(parent):
  67.     "A red rectangle that selects the entities it touches."
  68.  
  69.     Hint = hintPlusInfobaselink("Rectangular selection of EVERYTHING||Rectangular selection of EVERYTHING:\n\nThis works like 'rectangular selection of polyhedron' (just on the left), but selects everything including duplicators and beziers.", "intro.mapeditor.toolpalettes.mousemodes.html#selecteverything")
  70.  
  71.     def rectanglesel(self, editor, x,y, rectangle):
  72.         if not ("T" in self.todo):
  73.             editor.layout.explorer.uniquesel = None
  74.         everythinglist = FindSelectable(editor.Root, None, [":e", ":p", ":d", ":b2"])
  75.         lastsel = None
  76.  
  77.         for o in everythinglist:
  78.             if o.type == ":p":
  79.                 if rectangle.intersects(o):
  80.                     o.selected = 1
  81.                     lastsel = o
  82.             else:
  83.                 org = o.origin
  84.                 if org is None: continue
  85.                 for f in rectangle.faces:
  86.                     if org*f.normal > f.dist:
  87.                         break
  88.                 else: # the point is inside the polyhedron
  89.                     o.selected = 1
  90.                     lastsel = o
  91.         
  92.         #
  93.         # add groups & brush entities whose selectable
  94.         #  subitems are all selected
  95.         #
  96.         brushentitylist = FindSelectable(editor.Root, None, [":g", ":b"])
  97.         for b in brushentitylist:
  98.             subitems = FindSelectable(b,None,[":e", ":p", ":d", ":b2"])
  99.             for e in subitems:
  100.                 if not e.selected:
  101.                     break
  102.             else:
  103.                 b.selected = 1
  104.  
  105.         if lastsel is not None:
  106.             editor.layout.explorer.focus = lastsel
  107.             editor.layout.explorer.selchanged()
  108.  
  109.  
  110. class CubeMakerDragObject(parent):
  111.     "A cube maker."
  112.  
  113.     Hint = hintPlusInfobaselink("Quick cube maker||Quick cube maker:\n\nAfter you click this button, you can draw rectangles on the map with the mouse button and these rectangles will be turned into actual cubes. This is a quick way to make a lot of cubes all around.", "intro.mapeditor.toolpalettes.mousemodes.html#createcube")
  114.  
  115.     def __init__(self, view, x, y, redcolor, todo):
  116.         parent.__init__(self, view, x, y, redcolor, todo)
  117.         self.pt0 = quarkpy.qhandles.aligntogrid(self.pt0, 1)
  118.         p = view.proj(self.pt0)
  119.         if p.visible:
  120.             self.x0 = p.x
  121.             self.y0 = p.y
  122.  
  123.     def buildredimages(self, x, y, flags):
  124.         depth = self.view.depth
  125.         p = self.view.proj(quarkpy.qhandles.aligntogrid(self.view.space(x, y, depth[0]), 1))
  126.         if p.visible:
  127.             x = p.x
  128.             y = p.y
  129.         dx = abs(self.x0-x)
  130.         dy = abs(self.y0-y)
  131.         if dx>dy: dx=dy
  132.         min = (depth[0]+depth[1]-dx)*0.5
  133.         p = self.view.proj(quarkpy.qhandles.aligntogrid(self.view.space(x, y, min), 1))
  134.         if p.visible:
  135.             min = p.z
  136.         max = min + dx
  137.         return parent.buildredimages(self, x, y, flags, (min,max))
  138.  
  139.     def rectanglesel(self, editor, x,y, rectangle):
  140.         for f in rectangle.faces:
  141.             #
  142.             # Prepare to set the default texture on the faces
  143.             #
  144.             f.texturename = "[auto]"
  145.             #
  146.             # Resize the texture so that their scales are 1,1 and their angles are 0,90.
  147.             #
  148.             tp = f.threepoints(0)
  149.             n = f.normal
  150.             v = orthogonalvect(n, editor.layout.views[0])
  151.             tp = (tp[0],
  152.                   v * 128 + tp[0],
  153.                   (n^v) * 128 + tp[0])
  154.             f.setthreepoints(tp, 0)
  155.  
  156.         quarkpy.mapbtns.dropitemsnow(editor, [rectangle], "new quick cube", "0")
  157.  
  158.  
  159. #
  160. # Cube Cutter routines
  161. #
  162.  
  163. def CubeCut(editor, face, choicefn=lambda face,n1: -abs(face.normal*n1)):
  164.     n = face.normal
  165.     sellist = editor.layout.explorer.sellist
  166.     if len(sellist)==1 and sellist[0].type==':f' and len(sellist[0].faceof):
  167.         #
  168.         # Special : if a face is selected, we build a special structure to fake
  169.         # the cutting of this face in two.
  170.         #
  171.         # First normalize the face texture.
  172.         #
  173.         center = n * face.dist
  174.         v = orthogonalvect(n, editor.layout.views[0])
  175.         #
  176.         # Do it !
  177.         #
  178.         undo = quarkx.action()
  179.         for poly in sellist[0].faceof:
  180.             newgroup = quarkx.newobj(poly.shortname+':g')
  181.             undo.put(poly.parent, newgroup, poly)
  182.             for f in poly.subitems:
  183.                 undo.move(f, newgroup)
  184.             for sign in (-128,128):
  185.                 newpoly = quarkx.newobj("piece:p")
  186.                 for spec,arg in poly.dictspec.items():
  187.                     newpoly[spec] = arg
  188.                 newface = sellist[0].copy()
  189.                 newpoly.appenditem(newface)
  190.                 newface = quarkx.newobj("cut:f")
  191.                 newface.texturename = quarkpy.mapbtns.textureof(editor)
  192.                 newface.setthreepoints((center, center + v * sign, center + (n^v) * 128), 0)
  193.                 newpoly.appenditem(newface)
  194.                 undo.put(newgroup, newpoly)
  195.             undo.exchange(poly, None)
  196.         undo.exchange(sellist[0], None)
  197.         editor.ok(undo, "cut face in two")
  198.         return
  199.  
  200.     sellist = editor.visualselection()
  201.     if len(sellist)==0:
  202.         sellist = [editor.Root]
  203.     elif len(sellist)==1 and sellist[0].type==':g':
  204.         #
  205.         # Special : if a group is selected, we just insert the new face in the group.
  206.         # First ask the user if he agrees...
  207.         #
  208.         todo = quarkx.msgbox("You are cutting a whole group in two parts. Do you want to try making two groups with a single global face for each group ? If you answer No, the selected polyhedrons will be individually cut in two parts.",
  209.           MT_CONFIRMATION, MB_YES_NO_CANCEL)
  210.         if todo == MR_CANCEL: return
  211.         if todo == MR_YES:
  212.             #
  213.             # First normalize the face texture.
  214.             #
  215.             center = n * face.dist
  216.             v = orthogonalvect(n, editor.layout.views[0])
  217.             #
  218.             # Do it !
  219.             #
  220.             undo = quarkx.action()
  221.             newgroup = sellist[0].copy()
  222.             newface = quarkx.newobj("cut:f")
  223.             newface.texturename = quarkpy.mapbtns.textureof(editor)
  224.             newface.setthreepoints((center, center - v * 128, center + (n^v) * 128), 0)
  225.             newgroup.appenditem(newface)
  226.             undo.put(sellist[0].parent, newgroup, sellist[0].nextingroup())
  227.             newface = quarkx.newobj("cut:f")
  228.             newface.texturename = quarkpy.mapbtns.textureof(editor)
  229.             newface.setthreepoints((center, center + v * 128, center + (n^v) * 128), 0)
  230.             undo.put(sellist[0], newface)
  231.             editor.ok(undo, "cut in two groups", [sellist[0], newgroup])
  232.             return
  233.  
  234.     polylist = []
  235.     for obj in sellist:
  236.         polylist = polylist + obj.findallsubitems("", ':p')    # find all polyhedrons
  237.     #
  238.     # Cut the polyhedrons in polylist
  239.     #
  240.     autoremove = polylist[:]
  241.     undo = quarkx.action()
  242.     for p in polylist:
  243.         if p.intersects(face):
  244.             #
  245.             # We must cut this polyhedron in two parts.
  246.             # Find a "model" face for the new one.
  247.             # This model face must be cutted in two.
  248.             #
  249.             bestface = None
  250.             minnormal = 9
  251.             for f1 in p.faces:
  252.                 vtx = f1.verticesof(p)
  253.                 gotv1 = vtx[-1]
  254.                 prevv = gotv1*n < face.dist
  255.                 for v in vtx:
  256.                     nextv = v*n < face.dist    # 0 or 1
  257.                     if prevv+nextv==1:   # the two vertices are not on the same side of the cutting plane
  258.                         gotv2 = v
  259.                         break
  260.                     prevv = nextv
  261.                     gotv1 = v
  262.                 else:
  263.                     continue   # this face doesn't cross the cutting plane
  264.                 test = choicefn(face, f1.normal)
  265.                 if test<minnormal:
  266.                     minnormal = test
  267.                     bestface = f1
  268.                     bestv1 = gotv1
  269.                     bestv2 = gotv2
  270.             if bestface is None:
  271.                 continue   # should not occur
  272.             #
  273.             # Build the new face.
  274.             #
  275.             newface = bestface.copy()
  276.             newface.shortname = "cut"
  277.             #
  278.             # Rotate the face to its correct position.
  279.             #
  280.             f = (bestv2*n - face.dist) / (bestv2*n - bestv1*n)
  281.             center = bestv1*f + bestv2*(1-f)   # the intersection of the edge "bestv1->bestv2" with the plane
  282.             newface.distortion(n, center)
  283.             #
  284.             # Insert the new face into the polyhedron.
  285.             #
  286.             undo.put(p, newface)
  287.             #
  288.             # Do it again with the other orientation to make the other half.
  289.             #
  290.             p2 = p.copy()
  291.             newface = bestface.copy()
  292.             newface.shortname = "cut"
  293.             newface.distortion(-n, center)
  294.             p2.appenditem(newface)
  295.             undo.put(p.parent, p2, p.nextingroup())
  296.             autoremove.append(p2)
  297.  
  298.     editor.ok(undo, "cut in two", autoremove)
  299.  
  300.  
  301. class CubeCutter(parent):
  302.     "Cuts polyhedrons in two parts along the line drawn."
  303.  
  304.     Hint = hintPlusInfobaselink("Cut faces, polyhedrons and groups in two||Cut faces, polyhedrons and groups in two:\n\nAfter you click this button, you can draw lines on the map with the mouse, and any polyhedron touching this line will be cut in two parts along it. This is a quick way to make complex shapes out of a single polyhedron. You can for example draw 3 lines in a wall to make the contour of a passage, and then just delete the middle polyhedron to actually make the hole.\n\nAdvanced features : if you select a face, it will be cut in two but not the other faces of the polyhedron (using \"face sharing\" techniques); and if you select a group, instead of cutting each polyhedron in two individually, QuArK will give you the option of making two copies of the whole group with the cutting plane as a shared face in each group. This lets you consider the cutting plane as a unique face and later move or rotate it to reshape all polyhedrons in the group at once.", "intro.mapeditor.toolpalettes.mousemodes.html#polycutter")
  305.  
  306.     def __init__(self, view, x, y, redcolor, todo):
  307.         parent.__init__(self, view, x, y, redcolor, todo)
  308.         self.pt0 = quarkpy.qhandles.aligntogrid(self.pt0, 1)
  309.         p = view.proj(self.pt0)
  310.         if p.visible:
  311.             self.x0 = p.x
  312.             self.y0 = p.y
  313.  
  314.     def buildredimages(self, x, y, flags):
  315.         p = self.view.proj(quarkpy.qhandles.aligntogrid(self.view.space(x, y, self.z0), 1))
  316.         if p.visible:
  317.             x = p.x
  318.             y = p.y
  319.         if x==self.x0 and y==self.y0:
  320.             return None, None
  321.         min, max = self.view.depth
  322.         min, max = min*0.99 + max*0.01, min*0.01 + max*0.99
  323.         face = quarkx.newfaceex([
  324.           self.view.space(self.x0, self.y0, min),
  325.           self.view.space(x, y, min),
  326.           self.view.space(x, y, max),
  327.           self.view.space(self.x0, self.y0, max)])
  328.         return None, [face]
  329.  
  330.     def rectanglesel(self, editor, x,y, face):
  331.         def choice1(face, n1, vertical=self.view.vector(self.pt0).normalized):   # vertical vector at this point
  332.             return abs(n1*vertical)
  333.         CubeCut(editor, face, choice1)
  334.  
  335.  
  336.  
  337. #class TextureCopier(parent):
  338. #    "Copy texture settings by dragging the mouse."
  339. #
  340. #    Hint = "copy texture settings by dragging the mouse"
  341.  
  342.  
  343. #
  344. # The tool bar with the available drag modes.
  345. # Add other drag modes from other plug-ins into this list :
  346. #
  347.             #(the_object                            ,icon_index)
  348. DragModes = [(quarkpy.maphandles.RectSelDragObject  ,0)
  349.             ,(EntRectSelDragObject                  ,1)
  350.             ,(EverythingRectSelDragObject           ,5)
  351.             ,(CubeMakerDragObject                   ,2)
  352.             ,(CubeCutter                            ,3)
  353.            #,(TextureCopier                         ,4)
  354.             ]
  355.  
  356. def selectmode(btn):
  357.     editor = mapeditor(SS_MAP)
  358.     if editor is None: return
  359.     try:
  360.         tb = editor.layout.toolbars["tb_dragmodes"]
  361.     except:
  362.         return
  363.     for b in tb.tb.buttons:
  364.         b.state = quarkpy.qtoolbar.normal
  365.     select1(btn, tb, editor)
  366.     quarkx.update(editor.form)
  367.     quarkx.setupsubset(SS_MAP, "Building").setint("DragMode", btn.i)
  368.  
  369. def select1(btn, toolbar, editor):
  370.     editor.MouseDragMode, dummyicon = DragModes[btn.i]
  371.     btn.state = quarkpy.qtoolbar.selected
  372.  
  373.  
  374. class DragModesBar(ToolBar):
  375.     "The new toolbar with DragModes buttons."
  376.  
  377.     Caption = "Mouse modes"
  378.  
  379.     def buildbuttons(self, layout):
  380.         btns = []
  381.         for i in range(len(DragModes)):
  382.             obj, icon = DragModes[i]
  383.             btn = qtoolbar.button(selectmode, obj.Hint, ico_dict['ico_dragmodes'], icon)
  384.             btn.i = i
  385.             btns.append(btn)
  386.         i = quarkx.setupsubset(SS_MAP, "Building").getint("DragMode")
  387.         if i>=len(DragModes): i=0
  388.         select1(btns[i], self, layout.editor)
  389.         return btns
  390.  
  391.  
  392.  
  393. #--- register the new toolbar ---
  394.  
  395. quarkpy.maptools.toolbars["tb_dragmodes"] = DragModesBar
  396.  
  397.  
  398. # ----------- REVISION HISTORY ------------
  399. #
  400. # $Log: mapdragmodes.py,v $
  401. # Revision 1.7  2003/03/15 20:40:19  cdunde
  402. # To update hints and add infobase links
  403. #
  404. # Revision 1.6  2001/10/22 10:15:48  tiglari
  405. # live pointer hunt, revise icon loading
  406. #
  407. # Revision 1.5  2001/04/15 22:48:17  tiglari
  408. # groups & brush entity selection in select everything mode (will be selected
  409. #  if all of their subitems are)
  410. #
  411. # Revision 1.4  2001/02/19 19:15:40  decker_dk
  412. # Added 'Select-everything'; entities, brushes, duplicators and beziers.
  413. #
  414. # Revision 1.3  2000/06/03 10:25:30  alexander
  415. # added cvs headers
  416. #
  417.  
  418.